<?php

declare(strict_types=1);

namespace App\CustHub\Service;

use App\CustHub\Amqp\Producers\CommonProducers;
use App\CustHub\Amqp\Producers\CommonTaskCenterProducers;
use App\CustHub\Amqp\Producers\SyncTaskProducers;
use App\CustHub\Constants\MsgTypeConstants;
use App\CustHub\Constants\RoomOpTypeConstants;
use App\CustHub\Constants\RoomTaskCenterTypeConstants;
use App\CustHub\Constants\SyncTaskStatusConstants;
use App\CustHub\Constants\SyncTaskTypeConstants;
use App\CustHub\Dto\CommonProducersDto;
use App\CustHub\Dto\RoomMemberDto;
use App\CustHub\Dto\RoomTcContentDto;
use App\CustHub\Dto\SmartTagStatsDto;
use App\CustHub\Dto\SyncTaskDto;
use App\CustHub\Event\RoomOperationBehaviorAfter;
use App\CustHub\Helpers\Robot;
use App\CustHub\Mapper\ChRobotMapper;
use App\CustHub\Mapper\ChRoomMapper;
use App\CustHub\Mapper\ChRoomMemberMapper;
use App\CustHub\Mapper\ChRoomPushMsgMapper;
use App\CustHub\Mapper\ChSmartTagMapper;
use App\CustHub\Mapper\ChSyncTaskMapper;
use App\CustHub\Model\ChConfigValue;
use App\CustHub\Model\ChRobot;
use App\CustHub\Model\ChRoom;
use App\CustHub\Model\ChRoomMember;
use App\CustHub\Model\ChRoomPushMsg;
use App\CustHub\Model\ChRoomRobot;
use App\CustHub\Model\ChSendRecord;
use App\CustHub\Model\ChSyncTask;
use App\Robot\Dto\RobotMsgDto;
use App\Robot\Job\SendRobotMsgDelayedJob;
use App\Robot\Job\SyncTaskJob;
use App\Robot\Service\RobotApiService;
use Carbon\Carbon;
use Hyperf\Amqp\Producer;
use Hyperf\Collection\Collection;
use Hyperf\Coroutine\Coroutine;
use Hyperf\Database\Model\Builder;
use Hyperf\Database\Model\Model;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Mine\Abstracts\AbstractService;
use Mine\Annotation\Transaction;
use Mine\Exception\NormalStatusException;
use Mine\MineCollection;
use Mine\MineModel;
use Mine\MineResponse;
use Psr\Http\Message\ResponseInterface;
use function Hyperf\Collection\collect;
use function Hyperf\Coroutine\co;

class ChRoomService extends AbstractService
{
    public $mapper;

    #[Inject]
    protected ChSyncTaskMapper $syncTaskMapper;

    #[Inject]
    protected ChRobotMapper $robotMapper;

    #[Inject]
    protected ChRoomMemberMapper $roomMemberMapper;

    #[Inject]
    protected ChRoomTaskCenterService $roomTaskCenterService;

    #[Inject]
    protected ChRoomPushMsgMapper $roomPushMsgMapper;

    public function __construct(ChRoomMapper $mapper)
    {
        $this->mapper = $mapper;
    }

    /**
     * 群列表
     * @param array $data
     * @return array
     */
    public function list(array $data): array
    {
        if(!empty($data['robot_id'])){
            $robot = $this->robotMapper->findById($data['robot_id']);
            $data['robot_user_id'] = [$robot->user_id];
        }

        if(!empty($data['user_id'])){
            $data['room_member_config'] = [
                'type' => 1,
                'user_id' => [$data['user_id']]
            ];
        }

        return $this->mapper->getRoomPageList($data);
    }

    public function export(array $params, ?string $dto, ?string $filename = null, ?\Closure $callbackData = null): ResponseInterface
    {
        if (empty($dto)) {
            return container()->get(MineResponse::class)->error('导出未指定DTO');
        }

        $filename = '群列表-'.Carbon::now()->toDateTimeString();

        $data = $this->mapper->getExportRoomList($params);

        foreach ($data as $k=>$item){
            $robots = [];
            if(!empty($item['robots'])){
                $robots = array_column($item['robots'],'nickname');
            }
            $data[$k]['robot_nickname'] = implode(',',$robots);

            $roomTags = [];
            if(!empty($item['room_tags'])){
                $roomTags = array_column($item['room_tags'],'name');
            }
            $data[$k]['room_tag_name'] = implode(',',$roomTags);

            $data[$k]['create_time'] = datetime($item['create_time']);

            $data[$k]['change_room_name'] = $item['room_is_forbid_change_name'] == 0 ? '关闭' : '开启';
            $data[$k]['invite_confirm'] = $item['room_is_invite_confirmation'] == 0 ? '关闭' : '开启';
        }

        return (new MineCollection())->export($dto, $filename, $data, $callbackData);
    }

    public function selectPageList(array $data): array
    {
        return $this->mapper->selectPageList($data);
    }

    public function roomDetail(array $data): MineModel|Model|string|Builder|null
    {
        $info = $this->mapper->model::query()
            ->select(['id','room_wxid','room_name','room_avatar_url','room_announcement','room_member_count','room_owner_name'])
            ->with(['admins' => function ($query) {
                $query->select(['room_wxid','nickname','avatar']);
            },'roomTags'=> function ($query) {
                $query->select(['id','name']);
            }])->where('id',$data['id'])
            ->first();

        return $info;
    }

    /**
     * 查询人数未满的群
     * @param array $ids
     * @param int $max 设定的多少人群满
     * @return MineModel|Model|string|Builder|null
     */
    public function getNotFullRobotRoomListByIds(array $ids,int $max): MineModel|Model|string|Builder|null
    {
        $info = $this->mapper->model::query()
            ->select(['id','room_wxid','room_name'])
            ->whereIn('room_wxid',$ids)
            ->where('room_member_count','<',$max)
            ->first();

        return $info;
    }

    public function getRoomMemberList(array $params)
    {
        if(!empty($params['id'])){
            $roomDetail = $this->mapper->findById($params['id']);
            if(empty($roomDetail)){
                throw new NormalStatusException('群不存在');
            }

            $params['room_wxid'] = $roomDetail['room_wxid'];
        }else{
            $params['room_wxid'] = ChRoom::query()
                ->where('main_uid',$params['main_uid'])
                ->pluck('room_wxid')
                ->toArray();
        }

        return $this->roomMemberMapper->getRoomMemberPageList($params);
    }


    /**
     * 群邀请分页列表
     * @param array $data
     * @return array
     */
    public function inviteList(array $data): array
    {
        $pageSize = $data['page_size'] ?? MineModel::PAGE_SIZE;
        $inviteIds = $this->roomMemberMapper->model::query()
            ->where('room_wxid',$data['room_wxid'])
            ->groupBy('inviter_id')
            ->pluck('inviter_id')->toArray();
        $keyword = $data['keyword'] ?? '';

        $result = $this->roomMemberMapper->model::query()
            ->select(['id','user_id','nickname','avatar'])
            ->where('room_wxid',$data['room_wxid'])
            ->whereIn('user_id',$inviteIds)
            ->when(!empty($keyword),function ($query) use ($keyword){
                if(is_numeric($keyword)){
                    $query->where('user_id',$keyword);
                }else{
                    $query->where('nickname','like','%'.$keyword.'%');
                }
            })
            ->orderBy('join_time','desc')
            ->paginate((int)$pageSize);
        return $this->mapper->setPaginate($result);
    }

    public function inviteDetails(array $data)
    {
        $keyword = $data['keyword'] ?? '';
        $isOut = $data['is_out'] ?? 0;
        $pageSize = $data['page_size'] ?? MineModel::PAGE_SIZE;
        $paginate = $this->roomMemberMapper->model::query()
            ->select(['id','user_id','corp_id','corp_name','nickname','avatar','join_time','is_out','out_time','member_type'])
//            ->with(['corp'=>function ($query) {
//                $query->select(['id','corp_name']);
//            }])
            ->where('room_wxid',$data['room_wxid'])
            ->where('inviter_id',$data['inviter_id'])
            ->when(!empty($keyword),function ($query) use ($keyword){
                if(is_numeric($keyword)){
                    $query->where('user_id',$keyword);
                }else{
                    $query->where('nickname','like','%'.$keyword.'%');
                }
            })->when(filled($isOut),function ($query) use ($isOut){
                $query->where('is_out',$isOut);
            })
            ->orderBy('join_time','desc')
            ->paginate((int)$pageSize);

        $paginate->getCollection()->transform(function ($item) {
            $item->join_time = datetime($item->join_time,'Y-m-d');
            $item->out_time = !empty($item->out_time) ? $item->out_time : '';
            $item->corp = [
                'id' => 0,
                'corp_name' => $item['corp_name']
            ];
            return $item;
        });

        return $this->mapper->setPaginate($paginate);
    }

    /**
     * 导出群成员数据
     * @param array $params
     * @return \Psr\Http\Message\ResponseInterface
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function exportRoomMember(array $params): \Psr\Http\Message\ResponseInterface
    {
        $roomWxid = $params['room_wxid'];
        $roomInfo = $this->mapper->findByRoomWxid($roomWxid,$params['main_uid']);
        if(empty($roomInfo)){
            throw new NormalStatusException('导出失败');
        }

        $tagName = $roomInfo['room_name'];

        $data = ChRoomMember::query()
            ->leftJoin('ch_room_member as b', function ($join) use ($roomWxid) {
                $join->on('ch_room_member.user_id', '=', 'b.inviter_id')
                    ->where('b.room_wxid', '=', $roomWxid);
            })
            ->leftJoin('ch_room_member as c', function ($join) use ($roomWxid) {
                $join->on('ch_room_member.inviter_id', '=', 'c.user_id')
                    ->where('c.room_wxid', '=', $roomWxid);
            })
            ->where('ch_room_member.room_wxid', $roomWxid)
            ->select([
                'ch_room_member.nickname',
                'ch_room_member.is_out',
                'ch_room_member.join_time',
                'ch_room_member.out_time',
                'c.nickname as invite_nickname',
                Db::raw('COUNT(CASE WHEN b.nickname IS NOT NULL THEN 1 END) as invite_num'),
                Db::raw('COUNT(CASE WHEN b.is_out = 0 THEN 1 END) as inviter_total_in_group')
            ])
            ->groupBy('ch_room_member.id')
            ->get()->toArray();
        foreach ($data as $k=>$item){
            $data[$k]['join_time'] = !empty($item['join_time']) ? datetime($item['join_time']) : '';
            $data[$k]['out_time'] = !empty($item['out_time']) ? $item['out_time'] : '';
            $data[$k]['is_out'] = $item['is_out'] == 0 ? '仍在群' : '已退群';
        }
        return (new MineCollection())->export(RoomMemberDto::class, $tagName.'群成员数据', $data);
    }

    /**
     * 批量同步群
     * @param $robotIds
     * @return true
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function batchSyncRoom($robotIds): bool
    {
        $robotIds = json_decode($robotIds,true);
        foreach ($robotIds as $robotId){
            $this->addSyncTask($robotId);
        }
        return true;
    }

    /**
     * 同步群任务
     * @param $robotId
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function addSyncTask($robotId): bool
    {

        $type = SyncTaskTypeConstants::ROOM;
        $robotInfo = $this->robotMapper->findById($robotId);
        //检查是否存在进行中的任务
        if($this->syncTaskMapper->checkIsTaskInProgress($robotId,$type)){
//            throw new NormalStatusException('群正在同步中，请稍后再试');
            return false;
        }

//        $currentSeq = $this->syncTaskMapper->getLatestCurrentSeq($robotId,$type);

        $syncTaskData = [
            'uid' => $robotInfo['uid'],
            'main_uid' => $robotInfo['main_uid'],
            'corp_id' => $robotInfo['corp_id'],
            'robot_id' => $robotInfo['id'],
            'type' => $type,
            'status' => SyncTaskStatusConstants::RUNNING,
            'start_time' => time(),
        ];

//        $syncTaskId = $this->syncTaskMapper->save($syncTaskData);
        $info = ChSyncTask::create($syncTaskData);
        co(function () use ($info){
            try {
                container()->get(ChRoomSyncService::class)->syncRoom($info,0);
            }catch (\Throwable $throwable){
                ChSyncTask::query()
                    ->where('id',$info->id)
                    ->update([
                        'status' => SyncTaskStatusConstants::FAILURE,
                        'failure_reason' => $throwable->getMessage(),
                        'end_time' => time()
                    ]);
            }
        });
        return true;
//        return container()->get(Producer::class)->produce(new SyncTaskProducers(new SyncTaskDto(
//            taskId: $syncTaskId,
//            type: $type,
//            className: ChRoomSyncService::class,
//            method: 'syncRoom',
//        )));
    }

    public function reSync(array $data): void
    {
        $list = ChSyncTask::query()
            ->whereIn('status',[SyncTaskStatusConstants::WAITING,SyncTaskStatusConstants::RUNNING])
            ->get();

        co(function () use ($list){
            foreach ($list as $info){
                try {
                    $this->syncTaskMapper->update($info->id,[
                        'status' => SyncTaskStatusConstants::RUNNING,
                        'start_time' => time()
                    ]);
                    if($info->type == 4){
                        container()->get(ChRoomSyncService::class)->syncRoom($info,0);
                    }elseif ($info->type == 3){
                        container()->get(ChContactSyncService::class)->batchSyncContacts($info);
                    }
                }catch (\Throwable $throwable){
                    ChSyncTask::query()
                        ->where('id',$info->id)
                        ->update([
                            'status' => SyncTaskStatusConstants::FAILURE,
                            'failure_reason' => $throwable->getMessage(),
                            'end_time' => time()
                        ]);
                }
                Coroutine::sleep(randomFloat(3, 5));
            }
        });
    }

    /**
     * 创建群聊
     * @param int $uid
     * @param int $mainUid
     * @param mixed $robotId
     * @param array $memberList
     * @param string $roomName
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function createRoom(mixed $robotId,array $memberList,string $roomName = '')
    {
        $robotInfo = $this->robotMapper->findById($robotId);
        if(empty($robotInfo) || $robotInfo->login_status != 1){
            return false;
        }
        $robotApiService = make(\App\Common\Robot\Robot::class,[$robotInfo->server_id,$robotInfo->uuid])->gateway();
        $result = $robotApiService->createRoom($memberList);
        //群聊创建失败
        if($result === false){
            return false;
        }
        //群微信ID
        $roomWxid = $result['room_id'];

        $roomDetailList = $robotApiService->getRoomDetail([$roomWxid]);
        $roomDetail = array_shift($roomDetailList);

        container()->get(ChRoomSyncService::class)->saveRoom($robotApiService,$roomDetail,$robotInfo->uid,$robotInfo->main_uid,$robotId);
        if(!empty($roomName)){
            $robotApiService->editRoomName($roomWxid,$roomName);
        }
        return true;
    }

    /**
     * 同步群成员
     * @param array $params
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function syncRoomMember(array $params): bool
    {
        return container()->get(Producer::class)->produce(new CommonProducers(new CommonProducersDto(
            classname: ChRoomSyncService::class,
            method: 'syncRoomMember',
            data: [
                'room_wxid' => $params['room_wxid'],
                'main_uid' => $params['main_uid']
            ]
        )));
    }

    /**
     * 获取群内机器人列表
     * @param array $params
     * @return \Hyperf\Collection\Collection
     */
    public function getRoomRobotList(array $params): \hyperf\collection\collection
    {
        $id = $params['id'];
        $nickname = $params['nickname'] ?? '';

        $roomInfo = $this->mapper->model::query()
            ->select(['id','create_user_id'])
            ->with(['robots'=>function ($query) {
               $query->select(['id','nickname','avatar','corp_name','user_id','login_status']);
            }])
            ->where(function ($query) use ($id){
                if(is_array($id)){
                    $query->whereIn('id',$id);
                }else{
                    $query->where('id',$id);
                }
            })
            ->when($nickname,function ($query) use ($nickname){
                $query->whereHas('robots',function ($query) use ($nickname){
                    $query->where('nickname','like',"%{$nickname}%");
                });
            })
            ->first();

        if(empty($roomInfo)){
            throw new normalstatusexception('请先选择群');
        }

        $createuserid = $roomInfo->create_user_id;
        $robots = collect($roomInfo->robots)->map(function ($item) use ($createuserid){
            $item['is_owner'] = $item['user_id'] == $createuserid ? 1 : 0;
            return $item;
        });

        return $robots;
    }

    public function getRobotPageList(array $params)
    {
        $pageSize = $params['page_size'] ?? MineModel::PAGE_SIZE;
        $p = ChRoomRobot::query()
            ->where('room_id',$params['id'])
            ->with([
                'robot' => function ($query) {
                    $query->select(['id','nickname','avatar','corp_name','user_id','login_status']);
                }
            ])
            ->paginate((int)$pageSize);

        $p->getCollection()->transform(function ($item) {
            $item = array_merge(collect($item)->toArray(),collect($item['robot'])->toArray());
            unset($item['robot']);
            return $item;
        });

        return $this->mapper->setPaginate($p);
    }


    public function fetchCorpRoomMembers(array $params): array
    {
        $pageSize = $params['page_size'] ?? MineModel::PAGE_SIZE;
        $keyword = $params['keyword'] ?? '';
        $roomInfo = $this->mapper->findById($params['id']);
        if(empty($roomInfo)){
            throw new NormalStatusException('请先选择群');
        }
        $robotInfo = $this->robotMapper->findById($params['robot_id']);
        return $this->roomMemberMapper->fetchCorpRoomMembers($roomInfo->room_wxid,$robotInfo->user_id,$robotInfo->corp_id,$keyword,(int)$pageSize);
    }

    /**
     * 获取可转让的群聊
     * @param array $params
     * @return array
     */
    public function getTransferableRooms(array $params): array
    {
        $fromRobotInfo = $this->robotMapper->findById($params['from_robot_id']);
        $toRobotInfo = $this->robotMapper->findById($params['to_robot_id']);
        $params['from_robot_user_id'] = $fromRobotInfo->user_id;
        $params['to_robot_user_id'] = $toRobotInfo->user_id;
        return $this->mapper->getTransferableRooms($params);
    }

    /**
     * 获取可设置管理员的群聊
     * @param array $params
     * @return array
     */
    public function getAdminAssignableRooms(array $params):array
    {
        $fromRobotInfo = $this->robotMapper->findById($params['from_robot_id']);
        $toRobotInfo = $this->robotMapper->findById($params['to_robot_id']);
        $params['from_robot_user_id'] = $fromRobotInfo->user_id;
        $params['to_robot_user_id'] = $toRobotInfo->user_id;
        return $this->mapper->getAdminAssignableRooms($params);
    }

    /**
     * 转让群主
     * @param array $params
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \RedisException
     */
    public function changeRoomOwner(array $params): bool
    {
        $robotInfo = $this->robotMapper->findById($params['from_robot_id']);
        if(empty($robotInfo) || $robotInfo['login_status'] != 1){
            throw new NormalStatusException('请选择在线的机器人');
        }
        $toRobotInfo = $this->robotMapper->findById($params['to_robot_id']);
        if(empty($toRobotInfo)){
            throw new NormalStatusException('请选择要转让的机器人');
        }

        $newOwnerUserId = $toRobotInfo->user_id;

        $roomIds = json_decode($params['room_ids'],true);

        $roomTcContent = new RoomTcContentDto();
        $roomTcContent->setRoomIds($roomIds);
        $roomTcContent->setExtra([
            'robot_id' => $robotInfo->id,
            'newOwnerUserId' => $newOwnerUserId
        ]);
        return $this->roomTaskCenterService->addTask($params['uid'],$params['main_uid'],RoomTaskCenterTypeConstants::TRANSFER_ROOM_OWNER,$roomTcContent);
    }

    /**
     * 设置群管理员
     * @param array $params
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \RedisException
     */
    public function setRoomAdmin(array $params): bool
    {
        $robotInfo = $this->robotMapper->findById($params['from_robot_id']);
        if(empty($robotInfo) || $robotInfo['login_status'] != 1){
            throw new NormalStatusException('请选择在线的机器人');
        }
        $toRobotInfo = $this->robotMapper->findById($params['to_robot_id']);
        if(empty($toRobotInfo)){
            throw new NormalStatusException('请选择要转让的机器人');
        }

        $newOwnerUserId = $toRobotInfo->user_id;

        $roomIds = json_decode($params['room_ids'],true);

        $roomTcContent = new RoomTcContentDto();
        $roomTcContent->setRoomIds($roomIds);
        $roomTcContent->setExtra([
            'robot_id' => $robotInfo->id,
            'adminUserId' => $newOwnerUserId
        ]);
        return $this->roomTaskCenterService->addTask($params['uid'],$params['main_uid'],RoomTaskCenterTypeConstants::SET_ROOM_ADMIN,$roomTcContent);
    }

    /**
     * 修改群名称
     * @param array $params
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function editRoomName(array $params): bool
    {
        $roomIds = json_decode($params['room_ids'],true);
        $roomTcContent = new RoomTcContentDto();
        $roomTcContent->setRoomIds($roomIds);
        $roomTcContent->setExtra($params);
        return $this->roomTaskCenterService->addTask($params['uid'],$params['main_uid'],RoomTaskCenterTypeConstants::CHANGE_ROOM_NAME,$roomTcContent);
    }

    /**
     * 修改群名称
     * @param array $params
     * @return true
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function renameRoom(array $params): bool
    {
        $roomInfo = $this->mapper->findById($params['id']);
        $robot = $this->robotMapper->findById($params['robot_id']);
        if(empty($robot) || $robot['login_status'] != 1){
            throw new NormalStatusException('请选择在线的机器人');
        }

        $service = make(\App\Common\Robot\Robot::class,[$robot->server_id,$robot->uuid])->gateway();
        $result = $service->editRoomName($roomInfo->room_wxid,$params['room_name']);
        if($result === false){
            throw new NormalStatusException('修改群名称失败');
        }

        if($params['is_at_all']){
            // @所有人
            $msgData = new RobotMsgDto();
            $msgData->setMsgType(MsgTypeConstants::MIXED_TEXT)
                ->setContent([
                    [
                        'subtype' => 1,
                        'text' => ""
                    ]
                ])
                ->setToId($roomInfo->room_wxid)
                ->setServerId($robot->server_id)
                ->setUuid($robot->uuid);
            container()->get(LowQueueService::class)->push(new SendRobotMsgDelayedJob($msgData),1);
        }

        event(new RoomOperationBehaviorAfter($params['id'], RoomTaskCenterTypeConstants::CHANGE_ROOM_NAME, [
            'roomName' => $params['room_name'],
        ]));
        return true;
    }

    /**
     * 退群
     * @param array $params
     * @return bool
     */
    public function outRoom(array $params): bool
    {
        $roomInfo = $this->mapper->findById($params['id']);
        if(empty($roomInfo)){
            throw new NormalStatusException('请先选择群');
        }

        $robotInfo = $this->robotMapper->findById($params['robot_id']);
        if(empty($robotInfo) || $robotInfo['login_status'] != 1){
            throw new NormalStatusException('请选择在线的机器人');
        }

        $robotMemberInfo = $this->roomMemberMapper->findByRoomWxidAndUserId($roomInfo->room_wxid,$robotInfo->user_id);
        if(empty($robotMemberInfo) || $robotMemberInfo->is_owner){
            throw new NormalStatusException('群主不能退群');
        }

        Db::beginTransaction();
        try {
            //真实退群
            $robotService = make(\App\Common\Robot\Robot::class, [$robotInfo->server_id, $robotInfo->uuid])->gateway();
            $result = $robotService->outRoom($roomInfo->room_wxid);
            if($result === false){
                throw new NormalStatusException($robotService->getErrorMsg());
            }
            Db::commit();
            return true;
        }catch (\Throwable $e){
            Db::rollBack();
            throw new NormalStatusException($e->getMessage());
        }
    }

    /**
     * 批量退群
     * @param array $params
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function batchOutRoom(array $params): bool
    {
        $robotId = $params['robot_id'];
        $roomIds = json_decode($params['room_ids'],true);

        return container()->get(Producer::class)->produce(new CommonTaskCenterProducers(new CommonProducersDto(
            classname: ChRoomService::class,
            method: 'batchOutRoomHandler',
            data: [
                'robotId' => $robotId,
                'roomIds' => $roomIds
            ],
        )));

    }

    public function batchOutRoomHandler(array $data): void
    {
        $robotId = $data['robotId'];
        $roomIds = $data['roomIds'];

        $robotInfo = $this->robotMapper->findById($robotId);
        if(empty($robotInfo) || $robotInfo['login_status'] != 1){
            return;
        }

        $robotService = make(\App\Common\Robot\Robot::class, [$robotInfo->server_id, $robotInfo->uuid])->gateway();

        $roomList = $this->mapper->model::query()
            ->select(['id','robot_wxid'])
            ->whereIn('id',$roomIds)
            ->where('create_user_id','<>',$robotInfo->user_id)
            ->whereHas('robots',function ($query) use ($robotId){
                $query->where('robot_id',$robotId);
            })
            ->get();

        foreach ($roomList as $room){
            $robotService->outRoom($room->robot_wxid);
        }
    }


    /**
     * 保存到通讯录
     * @param array $params
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function saveContacts(array $params): bool
    {
        $roomIds = json_decode($params['room_ids'],true);

        $roomTcContent = new RoomTcContentDto();
        $roomTcContent->setRoomIds($roomIds);

        return $this->roomTaskCenterService->addTask($params['uid'],$params['main_uid'],RoomTaskCenterTypeConstants::SAVE_TO_CONTACTS,$roomTcContent);
    }

    /**
     * 取消保存通讯录
     * @param array $params
     * @return bool
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function removeFromContacts(array $params): bool
    {
        $roomIds = json_decode($params['room_ids'],true);

        $roomTcContent = new RoomTcContentDto();
        $roomTcContent->setRoomIds($roomIds);

        return $this->roomTaskCenterService->addTask($params['uid'],$params['main_uid'],RoomTaskCenterTypeConstants::UNSAVE_FROM_CONTACTS,$roomTcContent);
    }

    /**
     * 批量删除群
     * @param array $params
     * @return bool
     * @throws \Exception
     */
    #[Transaction]
    public function batchDelRoom(array $params): bool
    {
        $ids = json_decode($params['room_ids'],true);
        $roomList = ChRoom::query()->whereIn('id',$ids)->get();

        foreach ($roomList as $room){
            $room->robots()->detach();
            $room->roomMembers()->delete();
            $room->delete();
        }
        return true;
    }


    /**
     * 根据ids获取
     * @param $ids
     * @return array
     */
    public function getRoomsByUserIds($ids)
    {
        return $this->mapper->getModel()->newQuery()->whereIn('room_wxid', $ids)->distinct()->select(['room_name','room_wxid','customer_room_type','room_avatar_url'])->get()->toArray();
    }

    /**
     * 获取客户群二维码
     * @param array $params
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    public function getQrCode(array $params)
    {
        $id = $params['id'];

        $key = "get_room_qrcode:".$id;

        $qrCode = redis()->get($key);
        if($qrCode){
            return $qrCode;
        }

        $roomInfo = ChRoom::query()
            ->where('id',$id)
            ->whereHas('robots',function ($query){
                $query->where('login_status',1);
            })
            ->first();
        if(empty($roomInfo)){
            throw new NormalStatusException("请先登录机器人");
        }
        $robotId =  $roomInfo->robots()->value('id');

        $robotInfo = $this->robotMapper->findById($robotId);
        $robotApiService = make(\App\Common\Robot\Robot::class, [$robotInfo->server_id, $robotInfo->uuid])->gateway();
        $result = $robotApiService->generateRoomQRCode($roomInfo->room_wxid);
        if($result === false){
            throw new NormalStatusException("二维码获取失败");
        }
        $qrCode = $result['qrcode_data'];
        redis()->set($key,$qrCode,6 * 86400);
        return $qrCode;
    }


    public function getOwnerAndAdminRobotList(array $params)
    {
        $id = $params['id'];
        $mainUid = $params['main_uid'];
        $pageSize = empty($params['page_size']) ? MineModel::PAGE_SIZE : (int)$params['page_size'];

        $room = $this->mapper->findById($id);
        if (empty($room)){
            throw new NormalStatusException("请选择群聊~");
        }

        $list = Db::table('ch_room_member as a')
            ->selectRaw('b.id,b.nickname,b.avatar,b.corp_name,a.is_owner,a.is_admin')
            ->join('ch_robot as b',function ($join) use ($mainUid){
                $join->on( 'a.user_id', '=', 'b.user_id')->where('b.main_uid',$mainUid);
            })
            ->where('a.room_wxid',$room->room_wxid)
            ->where(function ($query){
                $query->where('a.is_owner',1)->orWhere('a.is_admin',1);
            })
            ->where('b.login_status',1)
            ->orderBy('a.is_owner','desc')
            ->paginate($pageSize);

        return $this->mapper->setPaginate($list);
    }

    /**
     * 设置客户群免打扰
     * @param array $params
     * @return array
     */
    public function setRoomDoNotDisturb(array $params): bool
    {
        $robotIds = is_array($params['robot_id']) ? $params['robot_id'] : json_decode($params['robot_id'],true);
        $roomTcContent = new RoomTcContentDto();
        $roomTcContent->setRoomIds([$params['id']]);
        $roomTcContent->setExtra([
            'robot_id' => $robotIds,
            'type' => $params['switch'] == 1 ? 3 : 4
        ]);
        return $this->roomTaskCenterService->addTask($params['uid'],$params['main_uid'],RoomTaskCenterTypeConstants::SET_ROOM_DO_NOT_DISTURB,$roomTcContent);
    }

    /**
     * 发布群公告
     * @param array $data
     * @return bool
     */
    public function publishRoomNotice(array $data): bool
    {
        $room = $this->getRoomInfo($data['id']);

        $robot = $this->robotMapper->findById($data['robot_id']);
        if(empty($robot) || $robot->login_status != 1){
            throw new NormalStatusException('请先登录企微号~');
        }

        $service = make(\App\Common\Robot\Robot::class,[$robot->server_id,$robot->uuid])->gateway();

        $rst = $service->postGrpAnn($room->room_wxid,$data['notice']);

        if($rst === false){
            throw new NormalStatusException($service->getErrorMsg());
        }

        return true;
    }

    private function getRoomInfo(mixed $id): Model|ChRoom|string|Builder
    {
        $room = $this->mapper->findById($id);
        if(empty($room)){
            throw new NormalStatusException("请选择群聊~");
        }
        return $room;
    }

    public function getRobotList(array $params)
    {
        $pageSize = $params['page_size'] ?? MineModel::PAGE_SIZE;

        if(empty($params['id'])){
            throw new NormalStatusException("请先选择群聊~");
        }
        $id = $params['id'];

        $nickname = $params['nickname'] ?? '';
        $login_status = $params['login_status'] ?? '';
        $p = ChRoomRobot::query()
            ->with([
                'robot' => function ($query) {
                    $query->select(['id','nickname','avatar','corp_name','user_id','login_status']);
                }
            ])
            ->where(function ($query) use ($id){
                if(is_array($id)){
                    $query->whereIn('room_id',$id);
                }else{
                    $query->where('room_id',$id);
                }
            })
            ->when($nickname,function ($query) use ($nickname){
                $query->whereHas('robot',function ($query) use ($nickname){
                    $query->where('nickname','like',"%{$nickname}%");
                });
            })
            ->when(filled($login_status),function ($query) use ($login_status){
                $query->whereHas('robot',function ($query) use ($login_status){
                    $query->where('login_status',$login_status);
                });
            })
            ->groupBy('robot_id')
            ->paginate((int)$pageSize);

        $p->getCollection()->transform(function ($item) {
            $item = array_merge(collect($item)->toArray(),collect($item['robot'])->toArray());
            unset($item['robot']);
            return $item;
        });

        $result = $this->mapper->setPaginate($p);
        $items = $result['items'];

        $robotIds = array_column($items,'id');

        //今日方案发送次数
        //todayMsgNum
        $todayStart = Carbon::now()->startOfDay()->toDateTimeString();
        $todayEnd = Carbon::now()->endOfDay()->toDateTimeString();

        $todaySendRecord = ChSendRecord::query()
            ->selectRaw('robot_id,count(id) as msg_num, count(DISTINCT to_id) as room_count')
            ->whereIn('robot_id',$robotIds)
            ->where('is_room',1)
            ->where('created_at','>=',$todayStart)
            ->where('created_at','<=',$todayEnd)
            ->groupBy('robot_id')
            ->get();

        $todaySendRecordMap = array_column($todaySendRecord->toArray(),null,'robot_id');
        $date = Carbon::now()->toDateString();

        $items = collect($items)->map(function ($item) use ($todaySendRecordMap,$date) {
            $item['num'] = 1;
            if(isset($todaySendRecordMap[$item['id']])){
                $item['today_msg_num'] = $todaySendRecordMap[$item['id']]['msg_num'];
                $item['today_room_num'] = $todaySendRecordMap[$item['id']]['room_count'];
            }else{
                $item['today_msg_num'] = 0;
                $item['today_room_num'] = 0;
            }
            $planNum = redis()->get('room_push_msg:'.$date.':'.$item['id']);
            $item['today_plan_num'] = empty($planNum) ? 0 : $planNum;
            return $item;
        });

        $result['items'] = $items->toArray();
        return $result;
    }

    public function getPushMsgList(array $params): array
    {
        $pageSize = empty($params['page_size']) ? MineModel::PAGE_SIZE : (int)$params['page_size'];
        $p = ChRoomPushMsg::query()
            ->where('main_uid',$params['main_uid'])
            ->with(['room'=>function ($query) {
                $query->select(['id','room_name','room_avatar_url']);
            },'robots'=>function ($query) {
                $query->select(['id','nickname','avatar','user_id','login_status']);
            }])
            ->orderBy('created_at','desc')
            ->paginate($pageSize);

        $p->getCollection()->transform(function ($item) {
            $item['content_num'] = count($item['content']);
            return $item;
        });

        $waitSendCount = ChRoomPushMsg::query()
            ->where('main_uid',$params['main_uid'])
            ->where('status','<>',0)
            ->count();

        $result = $this->roomPushMsgMapper->setPaginate($p);
        $result['pageInfo']['completedCount'] = $waitSendCount;
        return $result;
    }

    public function pushRoomMsg(array $data): bool
    {
        $data['room_id'] = $data['id'];

        $roomInfo = $this->mapper->findById($data['id']);

        $content = json_decode($data['content'],true);
        $robotIds = json_decode($data['robot_ids'],true);
        $data['content'] = $content;

        $this->roomPushMsgMapper->filterExecuteAttributes($data,true);
        /** @var ChRoomPushMsg $model */
        $model = $this->roomPushMsgMapper->model::create($data);

        if($model){
            $model->robots()->attach($robotIds);
            $id = $model->id;
            $needChangeStatus = false;
            $msgCount = count($content);
            $robotCount = count($robotIds);
            $date = Carbon::now()->toDateString();

            $sendRobotCount = 0;
            foreach ($robotIds as $k=>$robotId){
                $robotInfo = $this->robotMapper->findById($robotId);
                if(empty($robotInfo) || $robotInfo->login_status != 1){
                    continue;
                }
                $sendRobotCount = $sendRobotCount + 1;
                $delay = 0;
                $key = 'room_push_msg:'.$date.':'.$robotId;
                redis()->incr($key);
                redis()->expire($key,86400);
                foreach ($content as $ck => $msgContent) {
                    $msgData = new RobotMsgDto();
                    if($k+1 == $robotCount && $ck+1 == $msgCount){
                        $needChangeStatus = true;
                    }
                    $msgData->setMsgType($msgContent['msg_type'])
                        ->setContent($msgContent['content'])
                        ->setToId($roomInfo->room_wxid)
                        ->setFilename($msgContent['file_name'] ?? '')
                        ->setServerId($robotInfo->server_id)
                        ->setUuid($robotInfo->uuid)
                        ->setNeedChangeStatus($needChangeStatus)
                        ->setExtra([
                            'id' => $id,
                            'type' => 0
                        ]);
                    if($ck > 0){
                        $delay =  $delay + rand(2,5);
                    }
                    container()->get(LowQueueService::class)->push(new SendRobotMsgDelayedJob($msgData),$delay);
                }
            }

            if($sendRobotCount == 0){
                $this->roomPushMsgMapper->update($id,[
                    'status' => 2
                ]);
            }
            return true;
        }

        return false;
    }
}